﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DataModel;
using System.Data.Entity;
using DataAccess.Datatypes;

namespace DataAccess
{
    public class UserManager
    {
        private ModelContainer _data = new ModelContainer();

        //Devuelve todos los usuarios
        public IEnumerable<User> GetUsers()
        {
            List<User> result = new List<User>();

            foreach (var item in _data.UserSet)
            {
                User user = new User();
                user.id = item.id;
                user.Name = item.Name;
                result.Add(user);
            }

            return result;
        }

        //Agrega un usuario
        public void AddUser(string email, string socialId, string name, string password)
        {
            UserSet user = new UserSet();
            user.Email = email;
            user.SocialId = socialId;
            user.Name = name;
            user.Infractions = 0;
            user.Active = true;
            _data.SaveChanges();
        }

        public void AddUser(int id, string name)
        {
            try
            {
                UserSet user = new UserSet();
                user.id = id;
                user.Name = name;
                user.Infractions = 0;
                user.Active = true;
                _data.UserSet.Add(user);
            _data.SaveChanges();
            }
            catch (Exception)
            {
     
                throw;
            }
        }

        public void AddUser(int id, string name, string socialId)
        {
            try
            {
                
                UserSet user = new UserSet();
                user.id = id;
                user.Name = name;
                user.SocialId = socialId;
                user.Infractions = 0;
                user.Active = true;
                _data.UserSet.Add(user);
                _data.SaveChanges();
            }
            catch (Exception)
            {

                throw;
            }
        }

        //Devuelve una lista de deseos de un usuario
        public WishList GetWishList(int userId, int siteId)
        {
            var wishList = _data.WishSetSet.Where(w => w.UserId == userId && w.SiteId == siteId).Include(w => w.CriterionSet);

            WishList wish = new WishList();

            if (wishList.Count() != 0)
            {
                WishSet wishset = wishList.First();
                if (wishset != null)
                {
                    wish.id = wishset.id;
                    wish.Latitude = wishset.Latitude;
                    wish.Longitude = wishset.Longitude;
                    wish.Radius = wishset.Radius;
                    wish.Criterions = new List<Criterion>();

                    foreach (var item in wishset.CriterionSet)
                    {
                        Criterion crit = new Criterion();
                        crit.id = item.id;
                        crit.Attribute = item.SiteAttributeSet.Name;
                        crit.SiteAttributeId = item.SiteAttributeId;
                        crit.Value = item.Value;
                        wish.Criterions.Add(crit);
                    }
                }
            }

            return wish;
        }

        //Devuelve los deseos de una lista de deseos de un usuario
        public Wishes GetGoodsWishList(int userId, int siteId)
        {
            List<Good> result = new List<Good>();
            try
            {
                WishSet wish = _data.WishSetSet.Where(w => w.UserId == userId && w.SiteId == siteId).Include(w => w.CriterionSet).First();
                IEnumerable<GoodSet> goods = _data.GoodSet.Where(g => g.SiteId == siteId).Include(g => g.AttributeSet).ToList<GoodSet>();

                foreach (var item in goods)
                {
                    if (WishMatch(item, wish))
                    {
                        Good good = new Good();
                        good.Id = item.id;
                        good.Name = item.Name;
                        good.Latitude = item.Latitude;
                        good.Longitude = item.Longitude;
                        good.PriceDay = item.PriceDay;
                        good.Likes = item.Likes;
                        result.Add(good);
                    }
                }

                Wishes wishes = new Wishes();
                wishes.Goods = result;
                wishes.Latitude = wish.Latitude;
                wishes.Longitude = wish.Longitude;
                return wishes;
            }
            catch
            {
                return null;
            }
        }

        //Devuelve true si el bien corresponde con la lista de deseos del usuario
        public bool WishMatch(GoodSet good, WishSet wish)
        {
            IDictionary<int,CriterionSet> criterion = wish.CriterionSet.ToDictionary(c => c.SiteAttributeId);

            if (!IsAtRange(good, wish))
            {
                return false;
            }

            foreach (var item in good.AttributeSet)
            {
                if (criterion.ContainsKey(item.SiteAttributeId))
                {
                    CriterionSet crit = null;
                    if (criterion.TryGetValue(item.SiteAttributeId, out crit))
                    {
                        if (item.Value.ToLower().Contains(crit.Value.ToLower()))
                        {
                            return false;
                        }
                    }
                }
            }

            return true;
        }
        //Devuelve true si el bien esta en el radio de la wishlist
        public bool IsAtRange(GoodSet good, WishSet wish)
        {
            double theta = (double)(wish.Longitude - good.Longitude);
            double dist = (Math.Sin(deg2rad((double)good.Latitude)) * Math.Sin(deg2rad((double)wish.Latitude))
                            + Math.Cos(deg2rad((double)good.Latitude)) * Math.Cos(deg2rad((double)wish.Latitude)) * Math.Cos(deg2rad((double)theta)));
            dist = Math.Acos(dist);
            dist = rad2deg(dist);
            dist = dist * 60 * 1.852;
            
            return (dist <= (double)wish.Radius);
        }
        //Convierte Grados Decimales a Radianes
        private double deg2rad(double deg)
        {
            return (deg * Math.PI / 180.0);
        }
        //Convierte Radianes a Grados Decimales
        private double rad2deg(double rad)
        {
            return (rad / Math.PI * 180.0);
        }

        //Agrega una lista de deseos para un sitio a un usuario
        public long AddWishList(int userId, int siteId, decimal longitude, decimal latitude, decimal radius)
        {
            UserSet user = _data.UserSet.Find(userId);
            SiteSet site = _data.SiteSet.Find(siteId);
            if (user != null && site != null)
            {
                WishSet wish = new WishSet();
                wish.Latitude = latitude;
                wish.Longitude = longitude;
                wish.Radius = radius;
                wish.SiteId = siteId;
                wish.SiteSet = site;
                site.WishSet.Add(wish);
                wish.UserId = userId;
                wish.UserSet = user;
                user.WishSet.Add(wish);
                _data.SaveChanges();

                return wish.id;
            }

            return -1;
        }

        //Modifica una lista de deseos de un usuario
        public void ModifyWishList(int wishId, decimal longitude, decimal latitude, decimal radius)
        {
            WishSet wish = _data.WishSetSet.Find(wishId);
            if (wish != null)
            {
                wish.Latitude = latitude;
                wish.Longitude = longitude;
                wish.Radius = radius;
                _data.SaveChanges();
            }
        }

        //Agrega un criterio a una lista de deseos de un usuario
        public long AddCriterion(int userId, int siteId, int attributeId, string criterion)
        {
            WishSet wish = _data.WishSetSet.Where(w => w.UserId == userId && w.SiteId == siteId).First();
            SiteAttributeSet siteAtt = _data.SiteAttributeSet.Find(attributeId);
            if (wish != null && siteAtt != null)
            {
                CriterionSet crit = new CriterionSet();
                crit.Value = criterion;
                crit.SiteAttributeId = attributeId;
                crit.SiteAttributeSet = siteAtt;
                siteAtt.CriterionSet.Add(crit);
                crit.WishId = wish.id;
                crit.WishSet = wish;
                wish.CriterionSet.Add(crit);
                _data.SaveChanges();

                return crit.id;
            }

            return -1;
        }

        //Modifica un criterio
        public void ModifyCriterion(int critId, string value)
        {
            CriterionSet crit = _data.CriterionSetSet.Find(critId);
            if (crit != null)
            {
                crit.Value = value;
                _data.SaveChanges();
            }
        }

        //Elimina un criterio
        public void DeleteCriterion(int critId)
        {
            CriterionSet crit = _data.CriterionSetSet.Find(critId);
            if (crit != null)
            {
                crit.SiteAttributeSet.CriterionSet.Remove(crit);
                crit.WishSet.CriterionSet.Remove(crit);
                _data.CriterionSetSet.Remove(crit);
                _data.SaveChanges();
            }
        }

        //Bloquea a un usuario
        public void BlockUser(int userId)
        {
            UserSet user = _data.UserSet.Find(userId);
            if (user != null)
            {
                user.Active = false;
                _data.SaveChanges();
            }
        }

        //Devuelve una lista con los usuarios que han subido contenido inapropiado
        public IEnumerable<User> GetInfractors()
        {
            List<User> result = new List<User>();

            foreach (var item in _data.UserSet)
            {
                if (item.Infractions > 0)
                {
                    User user = new User();
                    user.id = item.id;
                    user.Name = item.Name;
                    user.Email = item.Email;
                    user.Active = item.Active;
                    user.Infractions = item.Infractions;
                    result.Add(user);
                }
            }

            return result;
        }

        //Devuelve true si el usuario esta activo (no bloqueado)
        public bool IsActiveUser(string userName)
        {
            try
            {
                UserSet user = _data.UserSet.Where(u => u.Name == userName).First();
                if (user != null)
                {
                    return user.Active;
                }

                return false;
            }
            catch
            {
                return false;
            }
            
        }

        //Invocado por Watchdog - bloquea contenidos y usuarios si exceden los limites permitidos y elimina rentas no activas
        public void Watchdog()
        {
            IEnumerable<ContentSet> contents = _data.ContentSet.ToList();
            ConfigurationSet config = _data.ConfigurationSet.First();
            //Reviso contenidos y desactivo los que llegan al limite
            foreach (var item in contents)
            {
                if (item.Active && item.InnapropiateSet.Count >= config.InapropiateMax)
                {
                    item.Active = false;
                    item.UserSet.Infractions++;
                    _data.SaveChanges();
                }
            }
            
            IEnumerable<UserSet> users = _data.UserSet.ToList();
            //Reviso usuarios y desactivo los que llegan al limite
            foreach (var item in users)
            {
                if (item.Active && item.Infractions >= config.InfractionMax)
                {
                    item.Active = false;
                    _data.SaveChanges();
                }
            }

            IEnumerable<RentSet> rents = _data.RentSetSet.ToList();
            //Reviso rentas y elimino las que no esten activas
            foreach (var item in rents)
            {
                if (!item.Active)
                {
                    item.GoodSet.RentSet.Remove(item);
                    item.UserSet.RentSet.Remove(item);
                    _data.RentSetSet.Remove(item);
                    _data.SaveChanges();
                }
            }
        }
        
    }
}
